﻿//#define TRACE_LAST
//#define TRACE_EVERY
//#define TRACE_LAST_PAUSE

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;



namespace ProjectEulerSolutions
{
    /*
     * A natural number, N, that can be written as the sum and product of a given set of at least two natural numbers, {a1, a2, ... , ak} is called a product-sum number: N = a1 + a2 + ... + ak = a1 × a2 × ... × ak.

For example, 6 = 1 + 2 + 3 = 1 × 2 × 3.

For a given set of size, k, we shall call the smallest N with this property a minimal product-sum number. The minimal product-sum numbers for sets of size, k = 2, 3, 4, 5, and 6 are as follows.

k=2: 4 = 2 × 2 = 2 + 2
k=3: 6 = 1 × 2 × 3 = 1 + 2 + 3
k=4: 8 = 1 × 1 × 2 × 4 = 1 + 1 + 2 + 4
k=5: 8 = 1 × 1 × 2 × 2 × 2 = 1 + 1 + 2 + 2 + 2
k=6: 12 = 1 × 1 × 1 × 1 × 2 × 6 = 1 + 1 + 1 + 1 + 2 + 6

Hence for 2≤k≤6, the sum of all the minimal product-sum numbers is 4+6+8+12 = 30; note that 8 is only counted once in the sum.

In fact, as the complete set of minimal product-sum numbers for 2≤k≤12 is {4, 6, 8, 12, 15, 16}, the sum is 61.

What is the sum of all the minimal product-sum numbers for 2≤k≤12000?

     * */
    class Problem88 : IProblem
    {
        public string Calculate()
        {
            //Uf... izgeneriramo sve na kraju.... problem je sto to nisam odma napravio.

            HashSet<long> mpsums = new HashSet<long>();

            int n = 12000;

            for (int k = 2; k <= n; k++)
            {
                long sum = MinimumProductSum(k);
#if TRACE_LAST_PAUSE
                            if (k % 20 == 0)
                                Console.ReadKey();
#endif
                mpsums.Add(sum);
            }

            //MinimumProductSum(108);

            return mpsums.Sum().ToString();
        }

        public long MinimumProductSum(int n)
        {
            if (n < 2)
                return 1;

            long min = int.MaxValue;

            long[] p = new long[n];
            for (int i = 0; i < p.Length; i++)
                p[i] = 1;

            int index = 0;
            int limit = n - 1;
            long sum = n;
            long product = 1;

            while (sum < 2 * n)
            {
                sum++;
                product /= p[index];
                p[index]++;
                product *= p[index];

                long difference = sum - product;

                if (difference > 0)
                {
                    index = (index + 1);
                    if (index > limit)
                    {
                        index--;
                        while (index > 0 && p[index] == p[index - 1])
                            index--;
                    }
                }
                else if (difference < 0)
                {
                    if (index == 0)
                    {
                        sum -= p[index];
                        product /= p[index];

                        p[index] = p[index + 1];

                        sum += p[index];
                        product *= p[index];

                        limit = index + 1;

                        long temp = p[limit];
                        while (temp == p[limit + 1])
                            limit++;


                        sum--;
                        product /= p[limit];
                        p[limit]--;
                        product *= p[limit];
                        limit--;
                    }
                    else
                    {
                        sum--;
                        product /= p[index];
                        p[index]--;
                        product *= p[index];
                        limit = index - 1;
                        index = 0;
                    }
                }
                else if (difference == 0)
                {
                    if (sum < min)
                    {
                        min = sum;
#if (TRACE_LAST)
                        Console.Write("k={0}: ", n);

                        for (int i = 0; i < p.Length; i++)
                        {
                            if (p[i] == 1)
                                break;

                            if (i == index && i == limit)
                                Console.ForegroundColor = ConsoleColor.Red;
                            else if (i == index)
                                Console.ForegroundColor = ConsoleColor.Green;
                            else if (i == limit)
                                Console.ForegroundColor = ConsoleColor.White;

                            Console.Write(p[i] + " ");
                            Console.ForegroundColor = ConsoleColor.Gray;
                        }
                        Console.WriteLine(" - sum={0}, product={1}, current minimum ", sum, product);
#endif
                    }
                }

#if (TRACE_EVERY)
                    Console.Write("k={0}: ", n);

                    for (int i = 0; i < p.Length; i++)
                    {
                        if (p[i] == 1)
                            break;

                        if (i == index && i == limit)
                            Console.ForegroundColor = ConsoleColor.Red;
                        else if (i == index)
                            Console.ForegroundColor = ConsoleColor.Green;
                        else if (i == limit)
                            Console.ForegroundColor = ConsoleColor.White;

                        Console.Write(p[i] + " ");
                        Console.ForegroundColor = ConsoleColor.Gray;
                    }
                    Console.WriteLine(" - sum={0}, product={1} ", sum, product);
#endif
            }


            return min;
        }
    }
}
